package br.com.simtecnologia.access.controll.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.core.MethodInfo;
import br.com.caelum.vraptor.http.ParameterNameProvider;
import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.ioc.RequestScoped;
import br.com.caelum.vraptor.resource.ResourceMethod;
import br.com.caelum.vraptor.view.Results;
import br.com.simtecnologia.access.controll.AccessLogicRouter;
import br.com.simtecnologia.access.controll.LogicRoute;
import br.com.simtecnologia.access.controll.handlers.AccessRouterResolverHandler;

@Component
@RequestScoped
public class DefaultAccessLogicRouter implements AccessLogicRouter {
	private static final long serialVersionUID = -7795459538140447427L;
	private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAccessLogicRouter.class);
	private static void debug(String regex, Object ... objects) {
		if (LOGGER.isDebugEnabled())
			LOGGER.debug(String.format(regex, objects));
	}
	private static final String LOGIN_FLOW_ID_PARAMETER_NAME = "loginFlowId";
	
	private final Result result;
	private final LogicRoute loginLogic;
	private final LogicRoute deniedLogic;
	private final ResourceMethod method;
	private final ParameterNameProvider parameterNameProvider;

	public DefaultAccessLogicRouter(AccessRouterResolverHandler resolver,
			MethodInfo methodInfo, Result result, ParameterNameProvider parameterName) {
		this.method = methodInfo.getResourceMethod();
		this.result = result;
		deniedLogic	= resolver.registerLogicRouteDenied(methodInfo.getResourceMethod());
		loginLogic	= resolver.registerLogicRouteLogin(methodInfo.getResourceMethod());
		this.parameterNameProvider = parameterName;
	}
	
	@Override
	public void redirectToDeniedPage() {
		if (deniedLogic == null) {
			LOGGER.warn(String.format("@DeniedLogic not found for logic [%s] sending page to Error 403 (Forbiding)",method.getMethod()));
			result.use(Results.http()).sendError(403);
		}else {
			debug("@DeniedLogin found, redirecting...");
			deniedLogic.redirectTo(result);
		}
	}
	
	@Override
	public void redirectToLoginPage() {
		redirectToLoginPage(null);
	}
	
	@Override
	public void redirectToLoginPage(Long loginFlowId) {
		if (loginLogic == null) {
			LOGGER.warn(String.format("@LoginLogic not found for logic [%s] sending page to error 403", method.getMethod()));
			result.use(Results.http()).sendError(403);
		} else {
			debug("@LoginLogic found, redirecting...");
			loginLogic.redirectTo(result,tryBuildSendLoginFlowId(loginFlowId));
		}
	}
	
	private Object[] tryBuildSendLoginFlowId(Long loginFlowId) {
		String[] parameterNames = parameterNameProvider.parameterNamesFor(loginLogic.getLogic());
		Class<?>[] parameterTypes = loginLogic.getLogic().getParameterTypes();
		Object[] parameters = loginLogic.getDefaultParameters();
		for (int i = 0; i < parameterNames.length; i++) {
			if (LOGIN_FLOW_ID_PARAMETER_NAME.equals(parameterNames[i]) &&
					acceptLong(parameterTypes[i]))
				parameters[i] = loginFlowId;
		}
		return parameters;
	}

	private boolean acceptLong(Class<?> targetParameterClass) {
		return targetParameterClass.isAssignableFrom(Long.class);
	}

	@Override
	public LogicRoute getLoginLogic() {
		return loginLogic;
	}
	@Override
	public LogicRoute getDeniedLogic() {
		return deniedLogic;
	}
}
